package en.kaist.cs408.acav;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

import android.media.AudioFormat;
import android.media.AudioRecord;
import android.media.MediaRecorder;
import android.os.Environment;
import android.util.Log;


public class VoiceRecorder {

	private static final int RECORDER_BPP = 16;
	private static final String AUDIO_RECORDER_FILE_EXT_WAV = ".wav";
	private static final String AUDIO_RECORDER_FOLDER = "ACAV";
	private static final String AUDIO_RECORDER_TEMP_FILE = "record_temp.raw";
	
	private static final int RECORDER_SAMPLERATE = 16000;
	//private static final int RECORDER_CHANNELS = AudioFormat.CHANNEL_IN_STEREO;
	private static final int RECORDER_CHANNELS = AudioFormat.CHANNEL_IN_MONO;
	private static final int RECORDER_AUDIO_ENCODING = AudioFormat.ENCODING_PCM_16BIT;;
	
	private AudioRecord recorder;
	private int bufferSize;
	private Thread recordingThread;
	private boolean isRecording;
	
	
	public VoiceRecorder (){
		recorder = null;
		bufferSize = AudioRecord.getMinBufferSize(RECORDER_SAMPLERATE, RECORDER_CHANNELS, RECORDER_AUDIO_ENCODING);
		recordingThread = null;
		isRecording = false;
	}
	
	public void startRecording(){
		Log.d("AudioRecord", "BufferSize: " + bufferSize);
		recorder = new AudioRecord (
				MediaRecorder.AudioSource.MIC,
				RECORDER_SAMPLERATE,
				RECORDER_CHANNELS,
				RECORDER_AUDIO_ENCODING,
				bufferSize);
		
		recorder.startRecording();
		isRecording = true;
		
		recordingThread = new Thread(new Runnable(){
			@Override
			public void run(){
				writeAudioDataToFile();
			}
		}, "AudioRecorder Thread");
		
		recordingThread.start();
	}
	
	public void stopRecording(){
		if(recorder != null){
			isRecording = false;
			recorder.stop();
			recorder.release();
			
			recorder = null;
			recordingThread = null;
		}
		
		//temp method
		checkTempFile(getTempFileName());
		
		copyWaveFile(getTempFileName(), getFileName());
		deleteFile(getTempFileName());
	}
	
	//temp method
	private void checkTempFile(String tempFileName){
		File sourceFile = new File(tempFileName);
		Log.d("AudioRecord", "TempFile length(in byte): "+sourceFile.length());
	}
	
	public String getFileName(){
		String filepath = Environment.getExternalStorageDirectory().getPath();
		File file = new File(filepath, AUDIO_RECORDER_FOLDER);
		
		if (!file.exists())
			file.mkdirs();
		
		return (file.getAbsolutePath()+"/"+"currentVoice"+AUDIO_RECORDER_FILE_EXT_WAV);
	}
	
	private String getTempFileName(){
		String filepath = Environment.getExternalStorageDirectory().getPath();
		File file = new File(filepath, AUDIO_RECORDER_FOLDER);
		
		if (!file.exists())
			file.mkdirs();
		
		File tempFile = new File(filepath, AUDIO_RECORDER_TEMP_FILE);
		
		if(tempFile.exists())
			tempFile.delete();
		
		return (file.getAbsolutePath()+"/"+AUDIO_RECORDER_TEMP_FILE);
	}
	
	
	
	private void writeAudioDataToFile(){
		byte data[] = new byte[bufferSize];
		String filename = getTempFileName();
		
		FileOutputStream os = null;
		
		try {
			os = new FileOutputStream(filename);
		} catch(FileNotFoundException e){
			e.printStackTrace();
		}
		
		int read = 0;
		
		if (null != os){
			while(isRecording) {
				read = recorder.read(data, 0, bufferSize);
				if(AudioRecord.ERROR_INVALID_OPERATION != read) {
					try {
						os.write(data);
					} catch (IOException e){
						e.printStackTrace();
					}
				} // end if
			} // end while
			
			try {
				os.close();
			} catch(IOException e){
				e.printStackTrace();
			}
		}
	}
	
	public void deleteFile(String fileName){
		File file = new File(fileName);
		file.delete();
	}
	
	private void copyWaveFile (String inFileName, String outFileName){
		FileInputStream in = null;
		FileOutputStream out = null;
		long totalAudioLen = 0;
		long totalDataLen = totalAudioLen + 36;
		long longSampleRate = RECORDER_SAMPLERATE;
		int channels = 1;
		long byteRate = RECORDER_BPP * RECORDER_SAMPLERATE * channels/8;
		
		byte[] data = new byte[bufferSize];
		
		try {
			in = new FileInputStream(inFileName);
			out = new FileOutputStream(outFileName);
			totalAudioLen = in.getChannel().size();
			totalDataLen = totalAudioLen + 36;
			
			Log.d("AudioRecord", "File size: " + totalDataLen);
			
			WriteWaveFileHeader(out, totalAudioLen, totalDataLen, longSampleRate, channels, byteRate);
			
			while(in.read(data) != -1)
				out.write(data);
			
			in.close();
			out.close();
		} catch (FileNotFoundException e){
			e.printStackTrace();
		} catch (IOException e){
			e.printStackTrace();
		}
	}
	
	private void WriteWaveFileHeader(FileOutputStream out, 
			long totalAudioLen, long totalDataLen, 
			long longSampleRate, int channels, 
			long byteRate) throws IOException {
		
		byte[] header = new byte[44];
		
		header[0] = 'R';
		header[1] = 'I';
		header[2] = 'F';
		header[3] = 'F';
		
		header[4] = (byte) (totalDataLen & 0xff);
		header[5] = (byte) ((totalDataLen >> 8) & 0xff);
		header[6] = (byte) ((totalDataLen >> 16) & 0xff);
		header[7] = (byte) ((totalDataLen >> 24) & 0xff);
		
		header[8] = 'W';
		header[9] = 'A';
		header[10] = 'V';
		header[11] = 'E';
		
		header[12] = 'f';
		header[13] = 'm';
		header[14] = 't';
		header[15] = ' ';
		
		header[16] = 16;
		header[17] = 0;
		header[18] = 0;
		header[19] = 0;
		header[20] = 1;
		header[21] = 0;

		header[22] = (byte)channels;
		header[23] = 0;
		
		header[24] = (byte) (longSampleRate & 0xff);
		header[25] = (byte) ((longSampleRate >> 8) & 0xff);
		header[26] = (byte) ((longSampleRate >> 16) & 0xff);
		header[27] = (byte) ((longSampleRate >> 24) & 0xff);
		
		header[28] = (byte) (byteRate & 0xff);
		header[29] = (byte) ((byteRate >> 8) & 0xff);
		header[30] = (byte) ((byteRate >> 16) & 0xff);
		header[31] = (byte) ((byteRate >> 24) & 0xff);
		
		header[32] = (byte) (1 * 16 / 8);
		header[33] = 0;
		header[34] = RECORDER_BPP;
		header[35] = 0;
		
		header[36] = 'd';
		header[37] = 'a';
		header[38] = 't';
		header[39] = 'a';
		
		header[40] = (byte) (totalAudioLen & 0xff);
		header[41] = (byte) ((totalAudioLen >> 8) & 0xff);
		header[42] = (byte) ((totalAudioLen >> 16) & 0xff);
		header[43] = (byte) ((totalAudioLen >> 24) & 0xff);
		
		out.write(header, 0, 44);
	}
}
